package com.example.es.springbootes.utils;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.MultiMatchQueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.SearchResultMapper;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.aggregation.impl.AggregatedPageImpl;
import org.springframework.data.elasticsearch.core.query.IndexQuery;
import org.springframework.data.elasticsearch.core.query.IndexQueryBuilder;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.SearchQuery;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageInfo;
import org.springframework.stereotype.Component;

/**
 * @ClassName: ESUtils
 * @Description: TODO
 * @author:
 * @date: 2019年7月24日 上午10:14:13
 */
@Component
public class EsHlUtils {

    @Autowired
	private ElasticsearchTemplate elasticsearchTemplate;


	/**
	 * 保存或更新
	 * <p>Title: saveOrUpdate</p>  
	 * <p>Description: </p>  
	 * @param id
	 * @param object
	 */
	public void saveOrUpdate(String id,Object object) {
		IndexQuery withObject = new IndexQueryBuilder().withId(id).withObject(object).build();
		elasticsearchTemplate.index(withObject);
	}
	
	
	/**
	 * 删除
	 * <p>Title: deleteById</p>  
	 * <p>Description: </p>  
	 * @param id
	 * @param clazz
	 */
	public void deleteById(String id,Class clazz) {
		elasticsearchTemplate.delete(clazz, id);
	}
	/**
	 * 
	 * <p>Title: search</p>  
	 * <p>Description: </p>  
	 * @param clazz    类
	 * @param pageNum   当前页
	 * @param pageSize  每页显示记录数
	 * @param sortDirection  排序的方向
	 * @param sortFields  排序字段
	 * @param mapboost   权重
	 * @param	Map<String, Float> fields = new HashMap<String, Float>();
	  * fields.put("name", 2f);
	  * fields.put("introduction", 10f);
	 * @param value  查询的值
	 * @param fields  查询字段  es中的字段
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public PageInfo search(Class clazz, Integer pageNum,
			Integer pageSize, String sortDirection, String sortFields, Map<String, Float> mapboost, String value,
			String... fields) {
		// 设置分页
		PageRequest pageable = null;
		// 加入排序的判断

		// 如果用户指定排序，就按指定参数进行排序
		if ("asc".equals(sortDirection)) {
			pageable = PageRequest.of(pageNum - 1, pageSize, Direction.ASC, sortFields);
		} else if ("desc".equals(sortDirection)) {
			pageable = PageRequest.of(pageNum - 1, pageSize, Direction.DESC, sortFields);
		} else {// 默认按 "_score" 排序
			pageable = PageRequest.of(pageNum - 1, pageSize);// sortFields==null?"_id":sortFields));
		}

		// 构建查询条件
		MultiMatchQueryBuilder queryBuilder = null;
		SearchQuery searchQuery = null;
		AggregatedPage page = null;

		if (value != null && !"".equals(value)) {

			queryBuilder = new MultiMatchQueryBuilder(value, fields);

			if (mapboost != null) {
				// 权重的问题
				queryBuilder.fields(mapboost);
			}

			// 创建高亮字段值
			HighlightBuilder.Field highlightFields[] = new HighlightBuilder.Field[fields.length];
			// 设置字段前后缀
			for (int i = 0; i < highlightFields.length; i++) {
				highlightFields[i] = new HighlightBuilder.Field(fields[i]).preTags("<font color='red'>")
						.postTags("</font>");
			}
			// 创建查询条件对象
			searchQuery = new NativeSearchQueryBuilder().withPageable(pageable).withQuery(queryBuilder)
					.withHighlightFields(highlightFields).build();

			// 执行查询效果
			page = elasticsearchTemplate.queryForPage(searchQuery, clazz, new SearchResultMapper() {

				@Override
				public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) {

					ObjectMapper objectMapper = new ObjectMapper();

					List<T> list = new ArrayList<>();

					// 命中数据
					SearchHits hits = response.getHits();

					long totalHits = hits.getTotalHits();

					// 命中的数据
					SearchHit[] hits2 = hits.getHits();
					try {

						for (SearchHit hit : hits2) {
							// 获取原数据的json数据
							String sourceAsString = hit.getSourceAsString();
							// json转换实体对象
							T entity = objectMapper.readValue(sourceAsString, clazz);
							// 获取高亮字段值
							Map<String, HighlightField> highlightFields2 = hit.getHighlightFields();

							for (int i = 0; i < fields.length; i++) {
								// 获取高亮的字段
								HighlightField highlightField = highlightFields2.get(fields[i]);
								if (highlightField != null) {
									// 获取高亮字段的内容数据
									Text fragment = highlightField.getFragments()[0];
									if (fragment != null) {
										// 反射获取的字段
										Field field = clazz.getDeclaredField(fields[i]);
										// 暴力反射
										field.setAccessible(true);
										// 设置地段对象的value值
										field.set(entity, fragment.toString());
									}
								}
							}

							list.add(entity);
						}
					} catch (Exception e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}

					return new AggregatedPageImpl<>(list, pageable, totalHits);
				}

				@Override
				public <T> T mapSearchHit(SearchHit searchHit, Class<T> aClass) {
					return null;
				}
			});

		} else {
			// 没有查询条件的的时候，获取es中的全部数据 分页获取
			searchQuery = new NativeSearchQueryBuilder().withPageable(pageable).build();
			page = elasticsearchTemplate.queryForPage(searchQuery, clazz);
		}

		// Mybatis中的分页对象
		Page page_list = new Page<>(pageNum, pageSize);
		// 加入集合
		page_list.addAll(page.getContent());
		// 设置总记录数
		page_list.setTotal(page.getTotalElements());
		// 转换成mybatis中的pageInfo对象
		PageInfo pageInfo = new PageInfo<>(page_list);

		// 返回值
		return pageInfo;
	}
}
